#include "Acceptor.h"
#include "Log.h"
#include <string.h>
#include <iostream>
#include <stdexcept>

using std::cout;

static void throwRuntimeError(const char* syscall)
{
    int savedErrno = errno;
    char buffer[256];
    const char* msg = strerror_r(savedErrno, buffer, sizeof(buffer));
    throw std::runtime_error(std::string(syscall) + " failed: " + msg);
}

Acceptor::Acceptor(const string &ip, unsigned int port)
    : _socket(), _address(ip, port)
{}

Acceptor::~Acceptor()
{
}

void Acceptor::ready()
{
    try
    {
        setReuseAddr();
        setReusePort();
        bind();
        listen();
    }
    catch (const std::exception &e)
    {
        Log::error ( "Acceptor setup failed: {}", e.what());
        throw;
    }
}

int Acceptor::accept()
{
    int fd = ::accept(_socket.fd(), nullptr, nullptr);
    if (fd < 0)
    {
        throwRuntimeError("accept");
    }
    return fd;
}

int Acceptor::fd()
{
    return _socket.fd();
}

void Acceptor::setReuseAddr()
{
    int opt = 1;
    int ret = setsockopt(_socket.fd(), SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if (ret < 0)
    {
        throwRuntimeError("setsockopt(SO_REUSEADDR)");
    }
}
void Acceptor::setReusePort()
{
    int opt = 1;
    int ret = setsockopt(_socket.fd(), SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
    if (ret < 0)
    {
        throwRuntimeError("setsockopt(SO_REUSEPORT)"); 
    }
}
void Acceptor::bind()
{
    int ret = ::bind(_socket.fd(), (struct sockaddr *)_address.getAddrinPtr(), sizeof(struct sockaddr));
    if (ret < 0)
    {
        throwRuntimeError("bind");
    }
}
void Acceptor::listen()
{
    int ret = ::listen(_socket.fd(), 128);
    if (ret < 0)
    {
        throwRuntimeError("listen");
    }
    cout << "server is listening......\n";
}